import { HttpException, HttpStatus, Inject, Injectable, Logger } from '@nestjs/common';
import { InjectEntityManager } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { EntityManager, Like, FindOneOptions } from 'typeorm';
import { RedisService } from '../redis/redis.service';
import { RegisterUserDto } from './dto/register-user.dto';
import * as crypto from 'crypto';
import { Role } from './entities/role.entity';
import { Permission } from './entities/permission.entity';
import { LoginUserDto } from './dto/login-user.dto';
import { LoginUserVo } from './vo/login-user.vo';
import { UpdateUserPasswordDto } from './dto/update-user-password.dto';
import { UpdateUserDto } from './dto/update-user.dto';

export function md5(str) {
  const hash = crypto.createHash('md5');
  hash.update(str);
  return hash.digest('hex');
}


@Injectable()
export class UserService {
  private logger = new Logger();
  @InjectEntityManager()
  private entityManager: EntityManager;
  @Inject(RedisService)
  private redisService: RedisService;





  async findUserDetailById(userId: number) {
    const user = await this.entityManager.findOne(User, {
      where: { id: userId }
    });

    return user;
  }
  async findUsers(username: string, nickName: string, email: string, pageNo: number, pageSize: number) {
    const skipCount = (pageNo - 1) * pageSize;
    const condition: Record<string, any> = {};

    if (username) { condition.username = Like(`%${username}%`); }
    if (nickName) { condition.nickName = Like(`%${nickName}%`); }
    if (email) { condition.email = Like(`%${email}%`); }

    const [users, totalCount] = await this.entityManager.findAndCount(User, {
      select: ['id', 'username', 'nickName', 'email', 'phoneNumber', 'isFrozen', 'headPic', 'createTime'],
      skip: skipCount,
      take: pageSize,
      where: condition
    });

    return {
      users,
      totalCount
    }
  }





  async updatePassword(passwordDto: UpdateUserPasswordDto) {
    const captcha = await this.redisService.get(`update_password_captcha_${passwordDto.email}`);
    if (!captcha || passwordDto.captcha !== captcha) { throw new HttpException('验证码有误', HttpStatus.BAD_REQUEST); }

    const foundUser = await this.entityManager.findOne(User, {
      where: { username: passwordDto.username },
      select: ['id','password','email']
    });  

    if(!foundUser || foundUser.email !== passwordDto.email){
      throw new HttpException('用户不存在或邮箱有误', HttpStatus.BAD_REQUEST);
    }
    
    foundUser.password = md5(passwordDto.password);

    try {
      await this.entityManager.update(User, foundUser.id , foundUser)
      await this.redisService.del(`update_password_captcha_${passwordDto.email}`);
      // await this.entityManager.save(foundUser);
      return '密码修改成功';
    } catch (e) {
      this.logger.error(e, UserService);
      return '密码修改失败';
    }

  }
  async update(userId: number, updateUserDto: UpdateUserDto) {
    const captcha = await this.redisService.get(`update_user_captcha_${updateUserDto.email}`);
    if (!captcha || updateUserDto.captcha !== captcha) { throw new HttpException('验证码有误', HttpStatus.BAD_REQUEST); }

    const foundUser = await this.entityManager.findOneBy(User, {
      id: userId
    });

    if (updateUserDto.nickName) { foundUser.nickName = updateUserDto.nickName; }
    if (updateUserDto.headPic) { foundUser.headPic = updateUserDto.headPic; }

    try {
      await this.entityManager.save(foundUser);
      await this.redisService.del(`update_user_captcha_${updateUserDto.email}`);
      return '用户信息修改成功';
    } catch (e) {
      this.logger.error(e, UserService);
      return '用户信息修改失败';
    }
  }
  async freezeUserById(id: number) {
    const user = await this.entityManager.findOne(User, {
      where: { id },
      select: ['isFrozen']
    });
    user.isFrozen = !user.isFrozen;
    await this.entityManager.update(User, id, user);
  }





  async login(loginUser: LoginUserDto, isAdmin: boolean) {

    const user = await this.entityManager.findOne(User, {
      where: {
        username: loginUser.username,
        isAdmin: isAdmin
      },
      relations: ['roles', 'roles.permissions']
    })

    if (!user || user.password !== md5(loginUser.password)) { throw new HttpException('用户不存在或密码错误', HttpStatus.BAD_REQUEST); }

    //返回数据
    const vo = new LoginUserVo();
    vo.userInfo = {
      id: user.id,
      username: user.username,
      nickName: user.nickName,
      email: user.email,
      phoneNumber: user.phoneNumber,
      headPic: user.headPic,
      createTime: user.createTime.getTime(),
      isFrozen: user.isFrozen,
      isAdmin: user.isAdmin,
      roles: user.roles.map(item => item.name),         //只保留角色名数组
      permissions: user.roles.reduce((arr, item) => {   //把角色里的permission存起来,过滤掉多角色里重复的权限
        item.permissions.forEach(permission => {
          if (!arr[permission['code']]) {
            arr[permission['code']] = permission['description'];
          }
        })
        return arr;
      }, {})
    }

    return vo;
  }
  async findUserById(userId: number, isAdmin: boolean) {
    const user = await this.entityManager.findOne(User, {
      where: {
        id: userId,
        isAdmin
      },
      relations: ['roles', 'roles.permissions']
    });

    return {
      id: user.id,
      username: user.username,
      email: user.email,
      isAdmin: user.isAdmin,
      roles: user.roles.map(item => item.name),
      permissions: user.roles.reduce((arr, item) => {
        item.permissions.forEach(permission => {
          if (arr.indexOf(permission) === -1) {
            arr.push(permission);
          }
        })
        return arr;
      }, [])
    }
  }
  async register(user: RegisterUserDto) {
    //边界处理
    const captcha = await this.redisService.get(`captcha_${user.email}`);
    if (!captcha || user.captcha !== captcha) {
      throw new HttpException('验证码有误', HttpStatus.BAD_REQUEST);
    }

    /*  or 查询  
        const finduser = await this.entityManager.findOne(User, {
          where: [
            { username: user.username },
            { email: user.email },
          ],
        }) 
    */

    const finduser = await this.finduserWithUsername(user.username);
    if (finduser) { throw new HttpException('用户名已被注册', HttpStatus.BAD_REQUEST); }

    const findemail = await this.finduserWithEmail(user.email);
    if (findemail) { throw new HttpException('邮箱已被注册', HttpStatus.BAD_REQUEST); }


    const newUser = new User();
    newUser.username = user.username;
    newUser.password = md5(user.password);
    newUser.email = user.email;
    newUser.nickName = user.nickName;

    try {
      await this.entityManager.insert(User, newUser);
      return '注册成功';
    } catch (e) {
      this.logger.error(e, UserService);
      return '注册失败';
    }

  }
  async finduserWithEmail(email: string) {

    return await this.entityManager.findOne(User, {
      where: { email: email },
      select: { id: true }
    })

  }
  async finduserWithUsername(username: string) {

    return await this.entityManager.findOne(User, {
      where: { username: username },
      select: { id: true }
    })

  }
  async initData() {
    const user1 = new User();
    user1.username = "zhangsan";
    user1.password = md5("111111");
    user1.email = "xxx@xx.com";
    user1.isAdmin = true;
    user1.nickName = '张三';
    user1.phoneNumber = '13333333333';

    const user2 = new User();
    user2.username = 'lisi';
    user2.password = md5("222222");
    user2.email = "yyy@yy.com";
    user2.nickName = '李四';

    const role1 = new Role();
    role1.name = '管理员';

    const role2 = new Role();
    role2.name = '普通用户';

    const permission1 = new Permission();
    permission1.code = 'ccc';
    permission1.description = '访问 ccc 接口';

    const permission2 = new Permission();
    permission2.code = 'ddd';
    permission2.description = '访问 ddd 接口';

    user1.roles = [role1];
    user2.roles = [role2];

    role1.permissions = [permission1, permission2];
    role2.permissions = [permission1];

    await this.entityManager.save(Permission, [permission1, permission2]);
    await this.entityManager.save(Role, [role1, role2]);
    await this.entityManager.save(User, [user1, user2]);
  }


}
